# Copyright 2009 Crisis Response International Inc.
# Derived from rietveld/codereview/views.py, Copyright 2008 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Views for mercyDMS."""

### Imports ###


# Python imports
import os
import cgi
import random
import re
import logging
import binascii
import datetime
import urllib
import md5
from xml.etree import ElementTree
from cStringIO import StringIO

# AppEngine imports
from google.appengine.api import mail
from google.appengine.api import memcache
from google.appengine.api import users
from google.appengine.api import urlfetch
from google.appengine.ext import db
from google.appengine.ext.db import djangoforms
from google.appengine.runtime import DeadlineExceededError
from google.appengine.runtime.apiproxy_errors import CapabilityDisabledError

# Django imports
# TODO(jamie): Don't import classes/functions directly.
from django import forms
from django.http import Http404
from django.http import HttpResponse, HttpResponseRedirect
from django.http import HttpResponseForbidden, HttpResponseNotFound
from django.shortcuts import render_to_response
import django.template
from django.utils import simplejson
from django.utils.safestring import mark_safe

# Local imports
from models import get_volunteer_by_user_id
import models
import forms
from common.choices import *


# Add our own template library.
#_library_name = __name__.rsplit('.', 1)[0] + '.library'
#if not django.template.libraries.get(_library_name, None):
#  django.template.add_to_builtins(_library_name)


### Constants ###

IS_DEV = os.environ['SERVER_SOFTWARE'].startswith('Dev')  # Development server

### Form classes ###
# class NewVolunteerForm(forms.Form):

#   title = forms.ChoiceField(required=False) ##, label='Title', choices=TITLE_CHOICES,
# ##                            widget=forms.Select(attrs={'class':'FormFields'}))
#   fname = forms.CharField(max_length=15) #max_length=15, widget=forms.TextInput(attrs={'class':'FormFields'}))
#   mname = forms.CharField(max_length=1) #max_length=1, widget=forms.TextInput(attrs={'class':'FormFields', 'size':1}))
#   lname = forms.CharField(max_length=20) #max_length=20, widget=forms.TextInput(attrs={'class':'FormFields'}))
#   gender = forms.ChoiceField(required=False, label='Gender', choices=[(None, 'Select'),('Male','Male'),('Female','Female')])
#   dob = forms.DateField(required=False)
#   address = forms.CharField(max_length=60)
#   city = forms.CharField(max_length=25)
#   state = forms.ChoiceField(choices=STATE_CHOICES)
#   zipcode = forms.CharField(max_length=5)
#   employment = forms.CharField(required=False, max_length=50, widget=forms.TextInput(attrs={'class':'FormFields','size':30}))
#   occupation = forms.CharField(required=False, max_length=50, widget=forms.TextInput(attrs={'class':'FormFields','size':30}))
#   email = forms.CharField(required=False,max_length=25)
#   email2 = forms.CharField(required=False, max_length=25)
#   hphone = forms.CharField(max_length=15)
#   wphone = forms.CharField(required=False, max_length=15)
#   cphone = forms.CharField(required=False, max_length=15)
#   aphone = forms.CharField(required=False, max_length=15)


# class VolunteerProfileForm(NewVolunteerForm):
#   have_passport = forms.BooleanField()


### Helper functions ###


# Counter displayed (by respond()) below) on every page showing how
# many requests the current incarnation has handled, not counting
# redirects.  Rendered by templates/base.html.
counter = 0


def respond(request, template, params=None):
  """Helper to render a response, passing standard stuff to the response.

  Args:
    request: The request object.
    template: The template name; '.html' is appended automatically.
    params: A dict giving the template parameters; modified in-place.

  Returns:
    Whatever render_to_response(template, params) returns.

  Raises:
    Whatever render_to_response(template, params) raises.
  """
  global counter
  counter += 1
  if params is None:
    params = {}
  #if request.user is not None:
  #  account = models.Account.current_user_account
  #  must_choose_nickname = not account.user_has_selected_nickname()
  #  uploadpy_hint = account.uploadpy_hint
  params['request'] = request
  params['counter'] = counter
  params['user'] = request.user
  params['is_admin'] = request.user_is_admin
  params['is_dev'] = IS_DEV
  full_path = request.get_full_path().encode('utf-8')
  if request.user is None:
    params['sign_in'] = users.create_login_url(full_path)
  else:
    params['sign_out'] = users.create_logout_url(full_path)
  try:
    return render_to_response(template, params)
  except DeadlineExceededError:
    logging.exception('DeadlineExceededError')
    return HttpResponse('DeadlineExceededError', status=503)
  except CapabilityDisabledError, err:
    logging.exception('CapabilityDisabledError: %s', err)
    return HttpResponse('Rietveld: App Engine is undergoing maintenance. '
                        'Please try again in a while. ' + str(err),
                        status=503)
  except MemoryError:
    logging.exception('MemoryError')
    return HttpResponse('MemoryError', status=503)
  except AssertionError:
    logging.exception('AssertionError')
    return HttpResponse('AssertionError')


def _random_bytes(n):
  """Helper returning a string of random bytes of given length."""
  return ''.join(map(chr, (random.randrange(256) for i in xrange(n))))


def _clean_int(value, default, min_value=None, max_value=None):
  """Helper to cast value to int and to clip it to min or max_value.

  Args:
    value: Any value (preferably something that can be casted to int).
    default: Default value to be used when type casting fails.
    min_value: Minimum allowed value (default: None).
    max_value: Maximum allowed value (default: None).

  Returns:
    An integer between min_value and max_value.
  """
  if not isinstance(value, (int, long)):
    try:
      value = int(value)
    except (TypeError, ValueError), err:
      value = default
  if min_value is not None:
    value = max(min_value, value)
  if max_value is not None:
    value = min(value, max_value)
  return value


### Decorators for request handlers ###

def login_required(func):
  """Decorator that redirects to the login page if you're not logged in."""

  def login_wrapper(request, *args, **kwds):
    if request.user is None:
      return HttpResponseRedirect(
          users.create_login_url(request.get_full_path().encode('utf-8')))
    return func(request, *args, **kwds)

  return login_wrapper


def admin_required(func):
  """Decorator that insists that you're logged in as administratior."""

  def admin_wrapper(request, *args, **kwds):
    if request.user is None:
      return HttpResponseRedirect(
          users.create_login_url(request.get_full_path().encode('utf-8')))
    if not request.user_is_admin:
      return HttpResponseForbidden('You must be admin in for this function')
    return func(request, *args, **kwds)

  return admin_wrapper


### Request handlers ###

@login_required
def index(request):
  if get_volunteer_by_user_id(request.user.user_id()) is None:
    return HttpResponseRedirect("/new")
  else:
    return HttpResponseRedirect("/volunteer/index")

@login_required
def new(request):
  """/new - Create a new volunteer.

  GET shows a blank form, POST processes it.
  """
  if request.method != 'POST':
    form = forms.NewVolunteerForm()
    return respond(request, 'new_volunteer_form.html', 
                   {'newForm': form,
                    'nickname': request.user.nickname(),
                    'logout': users.create_logout_url('/'),})

  form = forms.NewVolunteerForm(request.POST)
  volunteer = _make_new(request, form)
  if volunteer is None:
     return respond(request, 'new_volunteer_form.html', 
                    {'newForm': form,
                     'nickname': request.user.nickname(),
                     'logout': users.create_logout_url('/'),})
  else:
    return respond(request, 'thank_you_for_registering.html',
                 {'volunteer': get_volunteer_by_user_id(request.user.user_id()),
                  'logout': users.create_logout_url('/'),
                  })

class EmptyVolunteer(Exception):
  """Exception used inside _make_new() to break out of the transaction."""

def _make_new(request, form):
  """Helper for new().

  Return a valid volunteer, or None.
  """
  logging.info('form.is_valid():' + str(form.is_valid()))
  if not form.is_valid():
    return None

  def txn():
    volunteer = models.Volunteer(user = request.user,
                          user_id = request.user.user_id(),
                          title = form.cleaned_data['title'],
                          fname = form.cleaned_data['fname'],
                          mname = form.cleaned_data['mname'],
                          lname = form.cleaned_data['lname'],
                          address = form.cleaned_data['address'],
                          city = form.cleaned_data['city'],
                          state = form.cleaned_data['state'],
                          zipcode = form.cleaned_data['zipcode'],
                          email = form.cleaned_data['email'],
                          email2 = form.cleaned_data['email2'],
                          hphone = form.cleaned_data['hphone'],
                          wphone = form.cleaned_data['wphone'],
                          cphone = form.cleaned_data['cphone'],
                          aphone = form.cleaned_data['aphone'],)
    return volunteer.put()

  try:
    volunteer = db.run_in_transaction(txn)
  except EmptyVolunteer:
    errkey = url and 'url' or 'date'
    form.errors[errkey] = ['Volunteer contains no recognizable data']
    return None
  return volunteer


@login_required
def volunteer_index(request):
  return respond(request, 'volunteer_index.html',
                 {'nickname': request.user.nickname(),
                  'logout': users.create_logout_url('/'),
                  })

@login_required
def volunteer_profile(request):
  """/volunteer/profile - Edit a volunteer's profile

  GET shows a form, POST processes it.
  """
  volunteer = get_volunteer_by_user_id(request.user.user_id())
  prefset = models.PrefSet.get_or_insert('id' + str(volunteer.user_id), parent=volunteer)
  volunteer.prefset = prefset

  if request.method != 'POST':
      vol_form = forms.VolunteerProfileForm(instance=volunteer)
      pref_form = forms.VolunteerPrefsForm(instance=prefset)
      return respond(request, 'profile_edit_form.html', 
                              {'form': vol_form,
                               'prefForm': pref_form,
                               'nickname': volunteer.fname,
                               'logout': users.create_logout_url('/'),
                               })
  else:
    vol_form = forms.VolunteerProfileForm(request.POST, instance=volunteer)
    pref_form = forms.VolunteerPrefsForm(request.POST, instance=prefset)
    vol = _edit_profile(request, vol_form, pref_form, prefset)
    logging.info('vol from edit: ' + str(vol));
    if vol is None:
      return respond(request, 'profile_edit_form.html', 
                     {'form': form,
                      'prefForm': pref_form,
                      'nickname': volunteer.fname,
                      'logout': users.create_logout_url('/'),
                      })
    else:
      return HttpResponseRedirect('/')

def _edit_profile(request, vol_form, pref_form, prefset):
  """Helper for volunteer_profile().

  Return a valid volunteer, or None.
  """
  vol_form_is_valid = vol_form.is_valid()
  pref_form_is_valid = pref_form.is_valid()
  logging.info('volform.is_valid(): ' + str(vol_form_is_valid))
  logging.info('prefform.is_valid(): ' + str(pref_form_is_valid))
  if not vol_form_is_valid and not pref_form_is_valid:
    return None
  def txn():
    pref_form.save()
    return vol_form.save()
  try:
    volunteer = db.run_in_transaction(txn)
  except EmptyVolunteer:
    errkey = url and 'url' or 'date'
    form.errors[errkey] = ['Volunteer contains no recognizable data']
    return None
  return volunteer

@login_required
def volunteer_skills(request):
  """volunteer_skills - Update volunteer skill levels

  GET shows a form, POST processes it.
  """
  volunteer = get_volunteer_by_user_id(request.user.user_id())
  skillset = models.SkillSet.get_or_insert('id' + str(volunteer.user_id), parent=volunteer)
  volunteer.skillset = skillset

  if request.method != 'POST':
      skill_form = forms.VolunteerSkillsForm(instance=skillset)
      return respond(request, 'skills_edit_form.html', 
                              {'skillForm': skill_form,
                               'nickname': volunteer.fname,
                               'logout': users.create_logout_url('/'),
                               })
  else:
    skill_form = forms.VolunteerSkillsForm(request.POST, instance=skillset)
    vol = _edit_skills(request, skill_form, volunteer)
    if vol is None:
      return respond(request, 'skills_edit_form.html', 
                      {'skillForm': skill_form,})
    else:
      return HttpResponseRedirect('/')


def _edit_skills(request, skill_form, volunteer):
  """Helper for volunteer_skills().

  Return a valid volunteer, or None.
  """
  skill_form_is_valid = skill_form.is_valid()
  logging.info('skillform.is_valid(): ' + str(skill_form_is_valid))
  if not skill_form_is_valid:
    return None
  def txn():
    skill_form.save()
    return volunteer.put()
  try:
    volunteer = db.run_in_transaction(txn)
  except EmptyVolunteer:
    errkey = url and 'url' or 'date'
    form.errors[errkey] = ['Volunteer contains no recognizable data']
    return None
  return volunteer
